module mac #(
    parameter int unsigned A_BIT = 8,
    parameter int unsigned W_BIT = 8,
    parameter int unsigned B_BIT = 32
) (
    input  logic                    clk,
    input  logic                    rst_n,
    input  logic                    en,
    input  logic                    clr,
    input  logic        [A_BIT-1:0] x,
    input  logic signed [W_BIT-1:0] w,
    input  logic signed [B_BIT-1:0] acc_in,
    output logic signed [B_BIT-1:0] acc_out
);

    logic signed [B_BIT-1:0] acc_r;
    logic signed [B_BIT-1:0] prod;

    assign prod = $signed({1'b0, x}) * w;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            acc_r <= '0;
        end else if (en) begin
            if (clr) begin
                acc_r <= prod;
            end else begin
                acc_r <= acc_in + prod;
            end
        end
    end
    assign acc_out = acc_r;
endmodule

module mac_array #(
    parameter int unsigned P_ICH = 4,
    parameter int unsigned A_BIT = 8,
    parameter int unsigned W_BIT = 8,
    parameter int unsigned B_BIT = 32
) (
    input  logic                    clk,
    input  logic                    rst_n,
    input  logic                    en,
    input  logic                    clr,
    input  logic        [A_BIT-1:0] x_vec[P_ICH],
    input  logic signed [W_BIT-1:0] w_vec[P_ICH],
    output logic signed [B_BIT-1:0] acc
);

    logic        [A_BIT-1:0] x_pipe     [  P_ICH];
    logic signed [W_BIT-1:0] w_pipe     [  P_ICH];
    logic                    en_pipe    [  P_ICH];
    logic                    clr_pipe   [  P_ICH];

    logic signed [B_BIT-1:0] mac_cascade[P_ICH+1];

    assign mac_cascade[0] = '0;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (int i = 0; i < P_ICH; i++) begin
                x_pipe[i]   <= '0;
                w_pipe[i]   <= '0;
                en_pipe[i]  <= 1'b0;
                clr_pipe[i] <= 1'b0;
            end
        end else begin
            x_pipe[0]   <= x_vec[0];
            w_pipe[0]   <= w_vec[0];
            en_pipe[0]  <= en;
            clr_pipe[0] <= clr;

            for (int i = 1; i < P_ICH; i++) begin
                x_pipe[i]   <= x_pipe[i-1];
                w_pipe[i]   <= w_pipe[i-1];
                en_pipe[i]  <= en_pipe[i-1];
                clr_pipe[i] <= clr_pipe[i-1];
            end
        end
    end

    generate
        for (genvar i = 0; i < P_ICH; i++) begin : gen_mac
            mac #(
                .A_BIT(A_BIT),
                .W_BIT(W_BIT),
                .B_BIT(B_BIT)
            ) u_mac (
                .clk    (clk),
                .rst_n  (rst_n),
                .en     (en_pipe[i]),
                .clr    (clr_pipe[i] && (i == 0)),
                .x      (x_pipe[i]),
                .w      (w_pipe[i]),
                .acc_in (mac_cascade[i]),
                .acc_out(mac_cascade[i+1])
            );
        end
    endgenerate

    assign acc = mac_cascade[P_ICH];

endmodule
